package com.artfii.hibernate;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;

import javax.persistence.Id;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.Collection;
import java.util.Date;
import java.util.List;

public abstract class HibernateImpl<M extends java.io.Serializable, PK extends java.io.Serializable> implements BaseDao<M, PK> {


    protected final Class<M> entityClass;
    protected String pkName = null;

    public HibernateImpl() {
        this.entityClass = (Class<M>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        Field[] fields = this.entityClass.getDeclaredFields();
        for (Field f : fields) {
            if (f.isAnnotationPresent(Id.class)) {
                this.pkName = f.getName();
            }
        }
    }

    protected SessionFactory sessionFactory;
    protected Session session;

    public abstract void setSessionFactory(SessionFactory sessionFactory);

    public Session getSession() {
        if (session == null ) {
            session = sessionFactory.getCurrentSession();
        }
        return session;
    }

    public PK save(M model) {
        PK pk = (PK) getSession().save(model);
        session.flush();
        return pk;
    }

    public void saveOrUpdate(M model) {
        getSession().saveOrUpdate(model);
        session.flush();
    }

    public void update(M model) {
        getSession().merge(model);
        session.flush();
    }


    public void merge(M model) {
        getSession().merge(model);
        session.flush();
    }

    public void delete(PK id) {
        getSession().delete(this.get(id));
        session.flush();
    }

    public void deleteObject(M model) {
        getSession().delete(model);
        session.flush();
    }

    public M get(PK id) {
        if (id == null) {
            return null;
        } else {
            return (M) getSession().get(this.entityClass, id);
        }
    }

    public <M> List<M> list(final String hql, final Object[] paramlist) {
        return list(hql, -1, -1, paramlist);
    }

    private <M> List<M> list(final String hql, final int pn, final int pageSize, final Object[] paramlist) {
        Query query = getSession().createQuery(hql);
        setParameters(query, paramlist);
        if (pn >= 1 && pageSize >= 1) {
            query.setMaxResults(pageSize);
            int start = (pn - 1) * pageSize;
            if (start != 0) {
                query.setFirstResult(start);
            }
        }
        if (pn < 0) {
            query.setFirstResult(0);
        }
        List<M> results = query.list();
        return results;
    }

    private void setParameters(Query query, Object[] paramlist) {
        if (paramlist != null) {
            for (int i = 0; i < paramlist.length; i++) {
                if (paramlist[i] instanceof Date) {
                    query.setTimestamp(i, (Date) paramlist[i]);
                } else if (paramlist[i] instanceof Collection) {
                    query.setParameterList("params", (Collection) paramlist[i]);
                } else {
                    query.setParameter(i, paramlist[i]);
                }
            }


        }

    }


}